//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2007
//
// Highlight address attributes by color.
//

# include "StdAfx.h"
# include "Resource.h"
# include "DrawAddress.h"
# include "Map.h"
# include "Colors.h"
# include "Globals.h"

const size_t c_cAddressColors = 20;
const size_t c_cAddressExtraColors = 7;

static CColors * g_pAddressColors;

static const COLORREF g_colAddresses [c_cAddressExtraColors + c_cAddressColors] = {
	c_colBlack,  // contour: point
	c_colRed,    // fill: point, no address
	c_colRed,    // contour: point, incomplete address
	c_colWhite,  // contour: polyline 
	c_colRed,    // fill: polyline, no address
	c_colLtGray, // fill: polygon,  no address
	c_colRed,    // contour: polygon, incomplete address

	RGB (198,  16,  66),
	RGB (199,  62,  16),
	RGB (220, 119,  79),

	RGB (255, 128,  40),
	RGB (221, 151,   0),
	RGB (166, 166,   0),
	RGB (151, 200,  50),

	RGB (169, 251,   0),
	RGB (2,   253, 117),
	RGB (26,  189,   2),

	RGB (150, 224, 208),
	RGB (0,   217, 217),
	RGB (0,   166, 166),

	RGB (251,  94, 204),
	RGB (204,  94, 251),
	RGB (166,   0, 166),
	RGB (200,   0, 151),

	RGB (0,   128, 255),
	RGB (113, 113, 255),
	RGB (128,   0, 255),
};

void InitDrawAddress () {
	assert (g_pAddressColors == NULL);
	g_pAddressColors = new CColors;
	g_pAddressColors->LoadFromSetting ("AddressColors", g_colAddresses, ARRAY_SIZE (g_colAddresses));
}

void DestroyDrawAddress () {
	delete g_pAddressColors;
	g_pAddressColors = NULL;
}

bool AdjustColorsForAddressHighlighting (HWND _hWnd) {
	assert (g_pAddressColors != NULL);

	fstring str [c_cAddressExtraColors];
	str [0].LoadFromResource (IDS_AC_POINT_CONTOUR);
	str [1].LoadFromResource (IDS_AC_POINT_NO_ADDRESS);
	str [2].LoadFromResource (IDS_AC_POINT_INCOMPLETE);
	str [3].LoadFromResource (IDS_AC_POLYLINE_CONTOUR);
	str [4].LoadFromResource (IDS_AC_POLYLINE_NO_ADDRESS);
	str [5].LoadFromResource (IDS_AC_POLYGON_NO_ADDRESS);
	str [6].LoadFromResource (IDS_AC_POLYGON_INCOMPLETE);

	const size_t cColors = g_pAddressColors->GetColors ();
	const char ** const strColorNames = (const char **) alloca (sizeof (char *)*(cColors + c_cAddressExtraColors));

	size_t c;
	for (c = 0; c < c_cAddressExtraColors; ++ c)
		strColorNames [c] = str [c].c_str ();

	string_t strRandom;
	strRandom.LoadFromResource (IDS_AC_RANDOM);
	for (c = c_cAddressExtraColors; c < cColors; ++ c) {
		char * const strBuf = (char *) alloca (sizeof (char)*32);
		::sprintf (strBuf, strRandom.c_str (), c + 1 - c_cAddressExtraColors);
		strColorNames [c] = strBuf;
	}

	if (g_pAddressColors->Adjust (_hWnd, g_colAddresses, strColorNames, cColors)) {
		g_pAddressColors->SaveToSetting ("AddressColors");
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////

HPEN   GetPointAddressContourPen      () {return g_pAddressColors->GetPen   (0);}
HBRUSH GetPointNoAddressBrush         () {return g_pAddressColors->GetBrush (1);}
HPEN   GetPointIncompleteAddressPen   () {return g_pAddressColors->GetPen   (2, PS_DOT);}
HPEN   GetPolylineAddressContourPen   () {return g_pAddressColors->GetPen   (3, PS_SOLID, 3);}
HPEN   GetPolylineNoAddressPen        () {return g_pAddressColors->GetPen   (4);}
HBRUSH GetPolygonNoAddressBrush       () {return g_pAddressColors->GetBrush (5);}
HPEN   GetPolygonIncompleteAddressPen () {return g_pAddressColors->GetPen   (6, PS_DOT);}

static bool _LabelIsNumber (const string_t & _str) {
	if (_str.empty ())
		return false;
	return ::isdigit (_str [0]) != 0;
}

bool PolygonAddressIsIncomplete (const rgn_t & _rgn) {
	return _rgn.pExtra && ! _rgn.pExtra->strStreetDesc.empty () && _rgn.pExtra->strNumber.empty () && _LabelIsNumber (_rgn.strLabel);
}

bool PointAddressIsIncomplete (const rgn_t & _rgn) {
	return _rgn.pExtra->strStreetDesc.empty () != _rgn.pExtra->strNumber.empty ();
}

static
DWORD _GetStringHash (const string_t & _str) {
	DWORD dwHash = 0;
	const size_t cLen = _str.length ();
	for (size_t c = 0; c < cLen; ++ c)
		dwHash += static_cast<BYTE> (_str [c]);
	return dwHash + cLen;
}

static DWORD g_dwUsedHashValues [c_cAddressColors];
static size_t g_cUsedHashValues;

void StartDrawAddresses () {
	::memset (g_dwUsedHashValues, 0, sizeof (g_dwUsedHashValues));
	g_cUsedHashValues = 0;
}

static
DWORD _GetAddressHash (const rgn_t & _rgn) {
	if (! _rgn.pExtra)
		return -1;

	DWORD dwHash = _GetStringHash (_rgn.pExtra->strStreetDesc);
	if (_rgn.pExtra->pCity)
		dwHash += _GetStringHash (_rgn.pExtra->pCity->strName);
	if (_rgn.pExtra->pZip)
		dwHash += _GetStringHash (_rgn.pExtra->pZip->strName);

	if (dwHash == 0)
		return -1;

	DWORD dwIdx = dwHash % c_cAddressColors;
	if (g_dwUsedHashValues [dwIdx] == dwHash)
		// No collision.
		return dwIdx;

	// Resolve collision.
	if (g_cUsedHashValues == c_cAddressColors)
		// The table is overflow.
		return dwIdx;
	// Find next free cell.
	while (g_dwUsedHashValues [dwIdx] != 0 && g_dwUsedHashValues [dwIdx] != dwHash) {
		++ dwIdx;
		if (dwIdx >= c_cAddressColors)
			dwIdx = 0;
	}
	if (g_dwUsedHashValues [dwIdx] == dwHash)
		return dwIdx;

	// Occupy the cell.
	g_dwUsedHashValues [dwIdx] = dwHash;
	++ g_cUsedHashValues;
	return dwIdx;
}

HBRUSH AddressToBrush (const rgn_t & _rgn, RGN_KIND _Kind) {
	assert (g_pAddressColors != NULL);

	const DWORD dwHash = _GetAddressHash (_rgn);
	if (dwHash == -1) {
		if (_Kind == rkPoint)
			return GetPointNoAddressBrush ();
		else {
			assert (_Kind == rkPolygon);
			return GetPolygonNoAddressBrush ();
		}
	}
	return g_pAddressColors->GetBrush (c_cAddressExtraColors + dwHash);
}

HPEN AddressToPen (const rgn_t & _rgn) {
	assert (g_pAddressColors != NULL);

	const DWORD dwHash = _GetAddressHash (_rgn);
	if (dwHash == -1)
		return GetPolylineNoAddressPen ();
	return g_pAddressColors->GetPen (c_cAddressExtraColors + dwHash);
}
